Skip to main content

Fiber 架构

React 更换架构的原因是为了解决 React15React15 之前版本中渲染机制同步的问题,以及递归更新过程中无法中断的问题。

基础包结构

1。 react

react 基础包, 只提供定义 react 组件 (ReactElement) 的必要函数, 一般来说需要和渲染器 (react-dom,react-native) 一同使用。 在编写 react 应用的代码时, 大部分都是调用此包的 api

2。 react-dom

react 渲染器之一, 是 reactweb 平台连接的桥梁(可以在浏览器和 nodejs 环境中使用), 将 react-reconciler 中的运行结果输出到 web 界面上。 在编写react 应用的代码时,大多数场景下, 能用到此包的就是一个入口函数 ReactDOM.render(<App/>, document.getElementById('root')) , 其余使用的 api , 基本是 react 包提供的。

3。 react-reconciler

react 得以运行的核心包(综合协调 react-domreactscheduler 各包之间的调用与配合)。 管理 react 应用状态的输入和结果的输出。 将输入信号最终转换成输出信号传递给渲染器。

  • 接受输入 (scheduleUpdateOnFiber) , 将 fiber 树生成逻辑封装到一个回调函数中(涉及 fiber 树形结构, fiber.updateQueue 队列, 调和算法等)。
  • 把此回调函数( performSyncWorkOnRoot或performConcurrentWorkOnRoot )送入scheduler进行调度。
  • scheduler 会控制回调函数执行的时机, 回调函数执行完成后得到全新的 fiber 树。
  • 再调用渲染器(如 react-domreact-native 等)将 fiber 树形结构最终反映到界面上。

4。 scheduler

调度机制的核心实现, 控制由 react-reconciler 送入的回调函数的执行时机, 在concurrent 模式下可以实现任务分片。 在编写 react 应用的代码时, 同样几乎不会直接用到此包提供的 api

  • 核心任务就是执行回调(回调函数由 react-reconciler提供)。
  • 通过控制回调函数的执行时机, 来达到任务分片的目的, 实现可中断渲染(concurrent模式下才有此特性)。

架构分层

接口层(api)

内核层(core)

  1. 调度器 scheduler 包, 核心职责只有 1 个, 就是执行回调。
  • react-reconciler 提供的回调函数, 包装到一个任务对象中。
  • 在内部维护一个任务队列, 优先级高的排在最前面。
  • 循环消费任务队列, 直到队列清空。
  1. 构造器
  • react-reconciler 包, 有 3 个核心职责:
    1. 装载渲染器, 渲染器必须实现 HostConfig协议 (如: react-dom ), 保证在需要的时候, 能够正确调用渲染器的 api , 生成实际节点(如: dom节点 )。
    2. 接收 react-dom 包(初次 render )和 react 包(后续更新 setState )发起的更新请求。
    3. fiber 树的构造过程包装在一个回调函数中, 并将此回调函数传入到 scheduler 包等待调度。
  1. 渲染器
  • react-dom 包, 有 2 个核心职责:
    1. 引导 react 应用的启动(通过 ReactDOM.render )。
    2. 实现 HostConfig 协议(源码在 ReactDOMHostConfig.js 中), 能够将react-reconciler 包构造出来的 fiber 树表现出来, 生成 dom 节点(浏览器中), 生成字符串(ssr)。

react 中 有个重要的东西叫 workerloop scheduler和react-reconciler

区别

  • 任务调度循环是以二叉堆为数据结构(详见react 算法之堆排序), 循环执行堆的顶点, 直到堆被清空.
  • 任务调度循环的逻辑偏向宏观, 它调度的是每一个任务(task), 而不关心这个任务具体是干什么的(甚至可以将Scheduler包脱离react使用), 具体任务其实就是执行回调函数performSyncWorkOnRoot或performConcurrentWorkOnRoot.
  • fiber构造循环是以树为数据结构, 从上至下执行深度优先遍历(详见react 算法之深度优先遍历).
  • fiber构造循环的逻辑偏向具体实现, 它只是任务(task)的一部分(如performSyncWorkOnRoot包括: fiber树的构造, DOM渲染, 调度检测), 只负责fiber树的构造.

联系

fiber构造循环是任务调度循环中的任务(task)的一部分. 它们是从属关系, 每个任务都会重新构造一个fiber树.

优化点

scheduler包中调度原理, 也就是React两大工作循环中的任务调度循环. 并介绍了时间切片和可中断渲染等特性在任务调度循环中的实现. scheduler包是React运行时的心脏, 为了提升调度性能, 注册task之前, 在react-reconciler包中做了节流和防抖等措施.

主要逻辑

  • 输入: 将每一次更新(如: 新增, 删除, 修改节点之后)视为一次更新需求(目的是要更新DOM节点).
  • 注册调度任务: react-reconciler收到更新需求之后, 并不会立即构造fiber树, 而是去调度中心scheduler注册一个新任务task, 即把更新需求转换成一个task.
  • 执行调度任务(输出): 调度中心scheduler通过任务调度循环来执行task(task的执行过程又回到了react-reconciler包中).
    • fiber构造循环是task的实现环节之一, 循环完成之后会构造出最新的 fiber 树.
    • commitRoot是task的实现环节之二, 把最新的 fiber 树最终渲染到页面上, task完成.

优先级使用

就是 可中断渲染,时间切片(time slicing),异步渲染(suspense)等特性

React Context 原理

Context 提供了一种直接访问祖先节点上的状态的方法, 避免了多级组件层层传递props,而提高时间复杂度浪费性能。

  • 在消费状态时,ContextConsumer 节点调用 readContext(MyContext) 获取最新状态.
  • 在更新状态时, 由 ContextProvider 节点负责查找所有 ContextConsumer 节点, 并设置消费节点的父路径上所有节点的 fiber.childLanes , 保证消费节点可以得到更新.

useState

useStateuseReducer 原理基本一致 都属于状态类的hooks 只不过调用的方法不同, 分别是 mountState 和 mountReducer

mountState和mountReducer逻辑简单: 主要负责创建hook, 初始化hook的属性, 最后返回[当前状态, dispatch函数]

唯一的不同点是 hook.queue.lastRenderedReducer,mountReducer使用的是外部传入自定义reducer

  • 原理就是,在useState(initialState)函数内部, 设置hook.memoizedState = hook.baseState = initialState;, 初始状态被同时保存到了hook.baseState, hook.memoizedState中
  • hook.memoizedState: 当前状态
  • hook.baseState: 基础状态, 作为合并hook.baseQueue的初始值
  • 最后返回[hook.memoizedState, dispatch], 所以在function中使用的是hook.memoizedState.

合成事件

从架构上来讲, SyntheticEvent打通了从外部原生事件到内部fiber树的交互渠道, 使得react能够感知到浏览器提供的原生事件, 进而做出不同的响应, 修改fiber树, 变更视图等. 从实现上讲, 主要分为 3 步:

  • 监听原生事件: 对齐DOM元素和fiber元素
  • 收集listeners: 遍历fiber树, 收集所有监听本事件的listener函数.
  • 派发合成事件: 构造合成事件, 遍历listeners进行派发.